package com.suray.wcs.service.plc;

import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.entity.PlcDeviceDB;
import com.suray.basic.wcs.plc.mapper.PlcDeviceDBMapper;
import com.suray.basic.wcs.plc.pojo.oper.single.DeviceInterface;
import com.suray.basic.wcs.plc.pojo.oper.single.ScanOper;
import com.suray.basic.wcs.plc.pojo.oper.single.ShapeOper;
import com.suray.basic.wcs.plc.pojo.oper.single.WeightOper;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.commin.core.utils.StringUtils;
import com.suray.commin.enmus.DeviceStatus;
import com.suray.wcs.service.config.properties.WcsProperties;
import com.suray.wcs.service.core.util.DateUtil;
import com.suray.wcs.service.enums.NotificationTypeEnum;
import com.suray.wcs.service.init.SystemInit;
import com.suray.wcs.service.system.entity.PortDB;
import com.suray.wcs.service.system.service.IWareHouseDBService;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;
import com.suray.wcs.service.system.service.impl.WareHouseDBServiceImpl;
import com.suray.wcs.service.thread.process.PlcConveryTask;
import com.suray.wcs.service.util.WcsUtil;
import com.suray.wcs.service.wmsconnect.TaskApply;
import com.suray.wcs.service.wmsconnect.ToWMSImpl;
import com.suray.wcs.service.wmsconnect.WmsResponses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class SingleBarCodeScanner {

    @Autowired
    private static IWareHouseDBService wareHouseDBService = SpringContextHolder.getBean(WareHouseDBServiceImpl.class);
    private static ToWMSImpl toWMS = SpringContextHolder.getBean(ToWMSImpl.class);
    private static Logger log = LoggerFactory.getLogger(BarCodeScanner.class);

    public static volatile Map<PortDB, Boolean> scannerMap = new HashMap<>();

    private static PlcDeviceDBMapper plcdevicedbMapper = SpringContextHolder.getBean(PlcDeviceDBMapper.class);
    private static NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);

    public static void start(PortDB port) {
        PortDB realPort = SystemInit.PORTS.get(port.getPortId());
        //称重
        WeightOper weightOper = (WeightOper) Plc.deviceBaseNameMap.get(realPort.getWeightName() == null ? "" : realPort.getWeightName());
        //扫码
        ScanOper scanOper = (ScanOper) Plc.deviceBaseNameMap.get(realPort.getScanName() == null ? "" : realPort.getScanName());
        //外形检测
        ShapeOper shapeOper = (ShapeOper) Plc.deviceBaseNameMap.get(realPort.getShapeName() == null ? "" : realPort.getShapeName());
        try {
            double weightData = 0;
            String barCode = null;
            int heightType = 0;
            if ((scanOper != null && scanOper.isScanCanRead()) && scannerMap.get(port)) {
                log.info(port.getPortName() + "扫码或外形可读！");
                barCode = scanOper.getCodeData();
                log.info("{}_barCode:{}", scanOper.getDeviceName(), barCode);
                if (StringUtils.isBlank(barCode.trim())) {
                    scanOper.setScanError();
                    sendPlcBack(realPort, realPort.getPortName() + "," + scanOper.getDeviceName() + "条码不能为空");
                    return;
                }

                if (weightOper != null) {
                    weightData = weightOper.getWeightData();
                    log.info("{}_weightData:{}", weightOper.getDeviceName(), weightData);
                    if (weightData <= 0) {
                        weightOper.setWeightError();
                        sendPlcBack(realPort, realPort.getPortName() + "," + weightOper.getDeviceName() + "数据不符合");
                        return;
                    }

                    if (weightData > weightOper.getLimitWeightData()) {
                        weightOper.setWeightError();
                        sendPlcBack(realPort, realPort.getPortName() + "," + weightOper.getDeviceName()
                                + "超重, 当前重量：" + weightData + ", 极限重量:" + weightOper.getLimitWeightData());
                        return;
                    }
                }
                if (!StringUtils.isEmpty(port.getShapeName()) && shapeOper != null) {
                    heightType = shapeOper.getHeightType();
                    log.info("{}_heightType:{}", shapeOper.getDeviceName(), heightType);
                    int shapeResult = shapeOper.getResult();
                    if (shapeResult == 2) {
                        StringBuilder stringBuilder = new StringBuilder("外形检测不合格,外形检测结果:");
                        if (shapeOper.isCargoUpAlarm()) {
                            stringBuilder.append("超高！");
                        }
                        if (shapeOper.isCargoBeforeAlarm()) {
                            stringBuilder.append("前超限！");
                        }
                        if (shapeOper.isCargoAfterAlarm()) {
                            stringBuilder.append("后超限！");
                        }
                        if (shapeOper.isCargoLeftAlarm()) {
                            stringBuilder.append("左超限！");
                        }
                        if (shapeOper.isCargoRightAlarm()) {
                            stringBuilder.append("右超限！");
                        }
                        sendPlcBack(realPort,
                                "外形检测失败！" + stringBuilder);
                        return;
                    }
                    if (heightType == 0 || heightType > 3) {
                        sendPlcBack(realPort, "外形检测高度结果异常,结果为" + heightType);
                        return;
                    }
                }

                normalInCargo(weightData, barCode, heightType, realPort, scanOper);
                scannerMap.put(port, false);
            } else {
                if (scannerMap.get(port) && scanOper != null && !scanOper.isScanCanRead()) {
                    scanOper.setScanError();
                    sendPlcBack(realPort, realPort.getPortName() + "读取条码异常！");
                }
            }
        } catch (Exception e) {
            WcsUtil.notification(realPort.getPortName() + "称重、扫码、外形检测出现异常", NotificationTypeEnum.ERROR);
            log.warn(realPort.getPortName() + "称重、扫码、外形检测出现异常:{}", e);
            scannerMap.put(port, false);
        }
    }

    /**
     * 正常入库
     * @param weightData
     * @param barCode
     * @param heightType
     * @param realPort
     * @param scanOper
     * @throws IOException
     */
    private static void normalInCargo(double weightData, String barCode, int heightType, PortDB realPort, ScanOper scanOper)
            throws IOException {
        TaskApply taskApply = new TaskApply();
        taskApply.setCargoWeight(weightData);
        taskApply.setBarCode(barCode.trim());//清除空格
        taskApply.setCargoHeight(heightType);
        taskApply.setApplyTime(DateUtil.format(new Date(), DateUtil.YMDHMS));
        taskApply.setFromPort(realPort.getPortCode());
        // 未联机或者申请成功
        WmsResponses wmsResponses = toWMS.exeTaskApply(taskApply);
        if (wareHouseDBService.getCurWareHouseDB().getStatus().equals(DeviceStatus.DISABLE.getCode())
                || wmsResponses.getReturnStatus() == WcsProperties.SUCCESS) {
            if (wareHouseDBService.getCurWareHouseDB().getStatus().equals(DeviceStatus.DISABLE.getCode())) {
                log.info("{}单机模式默认成功", realPort.getPortName());
            } else {
                log.info("{}读取条码后，请求WMS成功", realPort.getPortName());
            }
            scannerMap.put(realPort, false);
        } else {
            scanOper.setNoTaskError();
            log.info("{}读取条码后，请求WMS失败", realPort.getPortName());
            sendPlcBack(realPort,
                    realPort.getPortName() + "条码值" + barCode + wmsResponses.getReturnInfo());
        }
    }

    /**
     * 回退
     * @param port
     * @param message
     */
    private static void sendPlcBack(PortDB port, String message) {
        try {
            LoggerUtil.info(message, SingleBarCodeScanner.class);
            if (StringUtils.isNotEmpty(port.getStations())) {
                String[] stationIds = port.getStations().split("-");
                PlcDeviceDB startPlcDb = plcdevicedbMapper.selectById(stationIds[0]);
                PlcDeviceDB endPlcDb = plcdevicedbMapper.selectById(stationIds[1]);
                DeviceInterface startDevice = (DeviceInterface) Plc.deviceBaseNameMap.get(startPlcDb.getDeviceName());
                DeviceInterface endDevice = (DeviceInterface) Plc.deviceBaseNameMap.get(endPlcDb.getDeviceName());
                PlcConveryTask.plcTransCargo(endDevice, startDevice, null, 9998);
                WcsUtil.changeNodeCargoStatus(endDevice.getCoord(), nodeDBService, false);
                scannerMap.put(port, false);
            } else {
                throw new RuntimeException("端口未配置输送路径");
            }
            WcsUtil.notification(message);
            // 备注字段标记LED显示屏主键
//            if (!StringUtils.isEmpty(port.getRemark())) {
//                LedShowData.ledSend(Integer.parseInt(port.getRemark()), "01", message);
//            }
        } catch (Exception e) {
            WcsUtil.notification(port.getPortName() + "回退异常，请人工处理:" + e.getMessage(), NotificationTypeEnum.ERROR);
            log.warn("回退逻辑错误！", port.getPortName(), e);
            scannerMap.put(port, false);
        }
    }
}
